/*
 * @(#)XfslGradientBasedAlgorithm.java        1.0 2000/05/09
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfsl.model.algorithm;

import xfuzzy.lang.*;
import xfuzzy.xfsl.model.*;
import xfuzzy.xfds.XfdsDataSet;

/**
 * Clase abstracta que describe un algoritmo de aprendizaje basado en gradiente
 * 
 * @author Francisco Jos� Moreno Velo
 *
 */
public abstract class XfslGradientBasedAlgorithm extends XfslAlgorithm implements Cloneable {

	//----------------------------------------------------------------------------//
	//                            COSTANTES P�BLICAS                              //
	//----------------------------------------------------------------------------//

	/**
	 * C�lculo directo de la derivada
	 */
	public static final int COMPUTE = 0;
	
	/**
	 * Estimaci�n basada en la secante
	 */
	public static final int ESTIMATE = 1;
	
	/**
	 * Estimaci�n estoc�stica
	 */
	public static final int STOCHASTIC = 2;

	//----------------------------------------------------------------------------//
	//                            MIEMBROS PRIVADOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Forma de c�lculo de la derivada
	 */
	private int derivative;
	
	/**
	 * Valor de perturbaci�n en el c�lculo por estimaci�n
	 */
	private double perturbation;

	//----------------------------------------------------------------------------//
	//                                CONSTRUCTOR                                 //
	//----------------------------------------------------------------------------//

	/**
	 * Constructor
	 */
	public XfslGradientBasedAlgorithm() {
		this.derivative = COMPUTE;
		this.perturbation = -1;
	}

	//----------------------------------------------------------------------------//
	//                             M�TODOS P�BLICOS                               //
	//----------------------------------------------------------------------------//

	/**
	 * Selecciona la opci�n desde el fichero de configuraci�n
	 */
	public void setOption(String name,double[] param) throws XflException {
		if(name.equals("weight_perturbation")) {
			derivative = ESTIMATE;
			perturbation = XfslAlgorithm.test(param[0], XfslAlgorithm.POSITIVE);
		}
		else if(name.equals("stochastic_perturbation")) {
			derivative = STOCHASTIC;
			perturbation = XfslAlgorithm.test(param[0], XfslAlgorithm.POSITIVE);
		}
	}

	/**
	 * Selecciona la opci�n desde la interfaz gr�fica
	 */
	public void setOption(int option,double param) throws XflException {
		if(option == COMPUTE) {
			derivative = COMPUTE;
		}
		if(option == ESTIMATE) {
			derivative = ESTIMATE;
			perturbation = XfslAlgorithm.test(param, XfslAlgorithm.POSITIVE);
		}
		else if(option == STOCHASTIC) {
			derivative = STOCHASTIC;
			perturbation = XfslAlgorithm.test(param, XfslAlgorithm.POSITIVE);
		}
	}
	
	/**
	 * Obtiene la forma de c�lculo del gradiente
	 */
	public int getOption() {
		return this.derivative;
	}
	
	/**
	 * Obtiene el valor de la perturbaci�n
	 * @return
	 */
	public double getPerturbation() {
		return this.perturbation;
	}
	
	/**
	 * Descripci�n de la opci�n en el fichero de configuraci�n
	 */
	public String toXML() {
		String eol = System.getProperty("line.separator", "\n");
		String xml = "";

		String name = "";
		try { name = getParseName( getCode() ); } catch(Exception ex) {}
		xml = "\t\t<algorithm name=\""+name+"\">"+eol;
		XfslAlgorithmParam[] param = getParams();
		for(int i=0; i<param.length; i++) {
			xml += "\t\t\t<param value=\""+param[i].getValue()+"\"></param>"+eol;
		}

		if(derivative == ESTIMATE) {
			xml = "\t\t<option name=\"weight_perturbation\">"+eol;
			xml += "\t\t\t<param value=\""+perturbation+"\" ></param>"+eol;
			xml += "\t\t</option>"+eol; 
		}
		if(derivative == STOCHASTIC) {
			xml = "\t\t<option name=\"stochastic_perturbation\">"+eol;
			xml += "\t\t\t<param value=\""+perturbation+"\" ></param>"+eol;
			xml += "\t\t</option>"+eol; 	
		}
		
		xml += "\t\t</algorithm>"+eol;

		return xml;
	}

	/**
	 * Calcula el gradiente de la funci�n de error
	 */
	public XfslEvaluation computeErrorGradient(Specification spec, XfdsDataSet pt,
			XfslErrorFunction ef) throws XflException {
		switch(derivative) {
			case COMPUTE: return ef.compute_derivatives(spec,pt);
			case ESTIMATE: return ef.estimate_derivatives(spec,pt,perturbation);
			case STOCHASTIC: return ef.stochastic_derivatives(spec,pt,perturbation);
			default: return null;
		}
	}

	/**
	 * Calcula el gradiente de las salidas
	 */
	public double[] computeOutputGradient(Specification spec,double[] input) throws XflException {
		switch(derivative) {
			case COMPUTE: return spec.compute_derivatives(input);
			case ESTIMATE: return spec.estimate_derivatives(input,perturbation);
			case STOCHASTIC: return spec.stochastic_derivatives(input,perturbation);
			default: return null;
		}
	}

}
